


#include "AllocationMemoryTable.h"

AllocationMemoryTable::AllocationMemoryTable(int parNumMaxSlot, MemoryWrapper* parMemory){

	numMaxSlot = parNumMaxSlot;
	numUsedSlot = 0;
	memory = parMemory;
	keyCounter = 0;
	weightSum =0;

	InformationTable infoTable;
	infoTable.countSubscriptions = 1;
	infoTable.countEvents = 1;
	infoTable.window = 1000;
	infoTable.resettable = true;
	parameterTable.insert(make_pair("unknown", infoTable));
	weightSum += infoTable.countSubscriptions * (infoTable.countEvents*infoTable.window);
	fixTable();

}

AllocationMemoryTable::~AllocationMemoryTable(){

}


void AllocationMemoryTable::updateTable(std::string type, int subscriptionCount, float window, bool resettable, int eventCount){


	boost::unordered_map<std::string, InformationTable>::iterator it = parameterTable.find(type);
	if (it != parameterTable.end()){
		it->second.countSubscriptions = subscriptionCount;
		it->second.window = window;
		it->second.resettable = resettable;
		it->second.countEvents = eventCount;
	} else {
		InformationTable informationTable;
		informationTable.countSubscriptions = subscriptionCount;
		informationTable.window = window;
		informationTable.resettable = resettable;
		if (type == "unknown") eventCount = 1;
		informationTable.countEvents = eventCount;
		parameterTable.insert(make_pair(type, informationTable));
	}
	weightSum += subscriptionCount * (eventCount*window);
}


void AllocationMemoryTable::fixTable(){
	cout << endl;
	cout << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" << endl;
	cout << "Allocation table: start allocating memory slots..." << endl;
	float avanzo = numMaxSlot;
	std::multiset<std::pair<int, boost::unordered_map<std::string, AllocTable>::iterator>, ClusterComparison > majorityChart;
	for (boost::unordered_map<std::string, InformationTable>::iterator it = parameterTable.begin(); it != parameterTable.end(); it++){
		boost::unordered_map<std::string, AllocTable>::iterator it2 = allocationTable.find(it->first);
		double nullInt;
		if (it2 != allocationTable.end()){
			float floatVal = ((it->second.countSubscriptions*(it->second.countEvents*it->second.window))/weightSum)*numMaxSlot;
			if (modf(floatVal, &nullInt) >= 0.5){
				it2->second.optimalSize = ceil(floatVal);
			}
			else {
				it2->second.optimalSize = floor(floatVal);
			}
			majorityChart.insert(make_pair(it2->second.optimalSize, it2));
			avanzo -= it2->second.optimalSize;
			it2->second.difference = it2->second.optimalSize - it2->second.actualSize;
			it2->second.resettable = it->second.resettable;
		} else {
			AllocTable allocTable;
			allocTable.actualSize = 0;
			float floatVal = ((it->second.countSubscriptions*(it->second.countEvents*it->second.window))/weightSum)*numMaxSlot;
			if (modf(floatVal, &nullInt) >= 0.5){
				allocTable.optimalSize = ceil(floatVal);
			}
			else {
				allocTable.optimalSize = floor(floatVal);
			}
			avanzo -= allocTable.optimalSize;
			allocTable.difference = allocTable.optimalSize - allocTable.actualSize;
			allocTable.resettable = it->second.resettable;
			std::pair<boost::unordered_map<std::string, AllocTable>::iterator, bool> ret =allocationTable.insert(make_pair(it->first, allocTable));
			majorityChart.insert(make_pair(allocTable.optimalSize, ret.first));
		}
	}
	cout << avanzo << endl;
	if (avanzo > 0){
		std::set<std::pair<int, boost::unordered_map<std::string, AllocTable>::iterator>, ClusterComparison >::iterator itChart = majorityChart.begin();
		while (avanzo != 0){
			cout << "plus: "<< avanzo << endl;
			float bonus;
			if (modf(avanzo/2, &bonus) != 0) bonus++;
			avanzo -= bonus;
			if (itChart != majorityChart.end()){
				itChart->second->second.optimalSize += bonus;
				itChart->second->second.difference = itChart->second->second.optimalSize - itChart->second->second.actualSize;
			}
			itChart++;
		}
	} else if (avanzo < 0){
		std::set<std::pair<int, boost::unordered_map<std::string, AllocTable>::iterator>, ClusterComparison >::reverse_iterator itChart = majorityChart.rbegin();
		while (avanzo != 0){
			cout << "avanzo: "<< avanzo << endl;
			float malus;
			if (modf(avanzo/2, &malus) != 0) malus--;
			//if (malus == 0) malus = -1;
			cout << "malus:" << malus << endl;
			if (itChart->second->second.optimalSize + malus >= 0){ //continue;
				avanzo -= malus;
				itChart->second->second.optimalSize += malus;
				itChart->second->second.difference = itChart->second->second.optimalSize - itChart->second->second.actualSize;
			}
			itChart++;
			if (itChart == majorityChart.rend()) sleep(10);
		}
	}
	for (boost::unordered_map<std::string, AllocTable>::iterator it = allocationTable.begin(); it != allocationTable.end(); it++){
		cout << "cluster: " << it->first << "\t\t";
		cout << "optimal: " << it->second.optimalSize << "\t";
		cout << "actual: " << it->second.actualSize << "\t";
		cout << "difference: " << it->second.difference << "\t";
		cout << "resettable: " << util::boolToString(it->second.resettable) << endl;
	}
	cout << endl;
	cout << "Allocation table: memory slots allocated." << endl;
	cout << "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^" << endl << endl;
}


void AllocationMemoryTable::resetTable(){

	oldParameterTable = parameterTable;

	cout << "UPDATE" << endl;
	cout << allocationTable.size() << endl;
	for (boost::unordered_map<std::string, AllocTable>::iterator it = allocationTable.begin(); it != allocationTable.end(); it++){
		boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it2 = clustersMap.find(it->first);
		if (it2 != clustersMap.end()){
			if (!it2->second.empty()){
				it->second.optimalSize = 0;
				it->second.difference = it->second.optimalSize - it->second.actualSize;
				oldAllocationTable.insert(make_pair(it->first, it->second));
			}
		}
	}
	cout << oldAllocationTable.size() << endl;

	oldClustersMap = clustersMap;
/*
	for (boost::unordered_map<std::string, AllocTable>::iterator it = oldAllocationTable.begin(); it != oldAllocationTable.end(); it++){
		it->second.optimalSize = 0;
		it->second.difference = it->second.optimalSize - it->second.actualSize;
	}
*/
	parameterTable.clear();
	weightSum = 0;
	allocationTable.clear();
	clustersMap.clear();

}

void AllocationMemoryTable::computeInsertion(PubPkt *pkt, std::string complexType){

	cout << "Memory manager: Allocation table: start message insertion..." << endl;

	if (numUsedSlot >= numMaxSlot) {
		cout << "Memory manager: Allocation table: capacity limit reached: need to remove a message." << endl;
		std::pair<timestamp_t, key_t> candidate;
		boost::unordered_map<std::string, AllocTable>::iterator pointerTocandidateType;
		int minDifference = 1;
		if (oldClustersMap.size() != 0){
			bool foundRemovable = false;
			bool considerNonResettability = true;
			do {
				for (boost::unordered_map<std::string, AllocTable>::iterator it = oldAllocationTable.begin(); it != oldAllocationTable.end(); it++){
					cout << "cluster: " << it->first << ": size " << it->second.actualSize << " resettable " << it->second.resettable << endl;
					if (it->second.actualSize==1 && !it->second.resettable && considerNonResettability) continue;
					cout << it->second.difference << endl;
					if (it->second.difference < minDifference){
						pointerTocandidateType = it;
						minDifference = it->second.difference;
						foundRemovable = true;
					}
				}
				if (!foundRemovable) { considerNonResettability = false; }
			} while (!foundRemovable);
			cout << "found" << endl;
			boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it2 = oldClustersMap.find(pointerTocandidateType->first);
			cout << it2->second.size() << endl;
			candidate = it2->second.top();
			cout << "found3" << endl;
			cout << "Memory manager: Allocation table: remotion from old cluster " <<  pointerTocandidateType->first << ": key "<< candidate.second << endl;
			memory->remove(candidate);
			it2->second.pop();
			pointerTocandidateType->second.actualSize--;
			pointerTocandidateType->second.difference++;
			numUsedSlot--;
			if (it2->second.empty()){ //if(pointerTocandidateType->second.actualSize == 0){
				//oldClustersMap.erase(it2);
				oldAllocationTable.erase(pointerTocandidateType);
				if (oldAllocationTable.empty()){ //if (oldClustersMap.empty()){
					oldClustersMap.clear();
					oldAllocationTable.clear();
					oldParameterTable.clear();
					cout << "Memory manager: Allocation table: old clusters completely resetted." << endl;
				}
			}
		} else {
			bool foundRemovable = false;
			bool considerNonResettability = true;
			do {
				for (boost::unordered_map<std::string, AllocTable>::iterator it = allocationTable.begin(); it != allocationTable.end(); it++){
					if (it->second.actualSize == 0) continue;
					cout << "cluster: " << it->first << ": size " << it->second.actualSize << " resettable " << it->second.resettable << endl;
					if (it->second.actualSize==1 && !it->second.resettable && considerNonResettability) continue;
					cout << it->second.difference << endl;
					if (it->second.difference < minDifference){
						pointerTocandidateType = it;
						minDifference = it->second.difference;
						foundRemovable = true;
					}
				}
				if (!foundRemovable) { considerNonResettability = false; }
			} while (!foundRemovable);
			boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it2 = clustersMap.find(pointerTocandidateType->first);
			candidate = it2->second.top();
			cout << "Memory manager: Allocation table: remotion from cluster " <<  pointerTocandidateType->first << ": key "<< candidate.second << endl;
			memory->remove(candidate);
			it2->second.pop();
			pointerTocandidateType->second.actualSize--;
			pointerTocandidateType->second.difference++;
			numUsedSlot--;
		/*	if (it2->second.empty()){
				clustersMap.erase(it2);
			}
		*/
		}
	}
	keyCounter++;
	timestamp_t eventTimestamp = pkt->getTimeStamp().getTimeVal();
	boost::unordered_map<std::string, AllocTable>::iterator it4 = allocationTable.find(complexType);
	if (it4 != allocationTable.end()){
		it4->second.actualSize++;
		it4->second.difference--;
		boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it5 = clustersMap.find(complexType);
		if (it5 != clustersMap.end()){
			cout << "Memory manager: Allocation table: insertion into cluster " << it5->first << ": key " << keyCounter << endl;
			it5->second.push(make_pair(eventTimestamp, keyCounter));
		} else {
			std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > queue;
			queue.push(make_pair(eventTimestamp, keyCounter));
			clustersMap.insert(make_pair(complexType, queue));
			cout << "Memory manager: Allocation table: created cluster " << complexType << endl;
			cout << "Memory manager: Allocation table: insertion into cluster " << complexType << ": key "<< keyCounter << endl;
		}
	} else {
		boost::unordered_map<std::string, AllocTable>::iterator it5 = allocationTable.find("unknown");
		it5->second.actualSize++;
		it5->second.difference--;
		boost::unordered_map<std::string, std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > >::iterator it6 = clustersMap.find("unknown");
		if (it6 != clustersMap.end()){
			it6->second.push(make_pair(eventTimestamp, keyCounter));
			cout << "Memory manager: Allocation table: insertion into cluster " << it6->first << ": key " << keyCounter << endl;
		} else {
			std::priority_queue<std::pair<timestamp_t, key_t>, std::vector<std::pair<timestamp_t, key_t> >, Comparison > queue;
			queue.push(make_pair(eventTimestamp, keyCounter));
			clustersMap.insert(make_pair("unknown", queue));
			cout << "Memory manager: Allocation table: created cluster unknown" << endl;
			cout << "Memory manager: Allocation table: insertion into cluster unknown: key "<< keyCounter << endl;
		}

	}
	memory->insert(pkt, keyCounter);
	numUsedSlot++;

	cout << "Memory manager: Allocation table: insertion terminated" << endl;

}

void AllocationMemoryTable::computeExtraction(BackSubPkt *pkt, std::set<PubPkt*> &resultSet){
	cout << "Memory manager: Allocation table: start messages extraction..." << endl;
	memory->extract(pkt->getEventType(), pkt->getConstraintList(), pkt->getConstraintsNum(), pkt->getTimeStamp().getTimeVal(), pkt->getWindow(), pkt->getOccurrence(), resultSet);
	cout << "Memory manager: Allocation table: extraction terminated." << endl;
}


